package VLine

import (
	"VLine/Structure/List"
	"fmt"
	"strconv"
)

const (
	FILE = iota
	BLOCK
	FOR
	FUNCTION
)

type Layer struct {
	description  int
	parent       *Layer
	count        int
	size         int
	constants    map[interface{}]int
	imports      map[string]int
	variables    map[string]int
	instructions []*Instruction
	functions    []*Function
	stack        *List.List
	breaks       []*Instruction
	continues    []*Instruction
}

func LayerNew(parent *Layer, description int) *Layer {
	var layer = &Layer{
		description: description,
		parent:      parent,
		variables:   make(map[string]int),
		count:       0,
		size:        0,
	}
	switch description {
	case FILE:
		layer.imports = make(map[string]int)
		layer.constants = make(map[interface{}]int)
		layer.instructions = make([]*Instruction, 0)
		layer.functions = make([]*Function, 0)
		layer.stack = List.New()
	case FUNCTION:
		layer.imports = parent.imports
		layer.constants = parent.constants
		layer.stack = parent.stack
		layer.functions = parent.functions
	case FOR:
		layer.imports = parent.imports
		layer.constants = parent.constants
		layer.instructions = parent.instructions
		layer.functions = parent.functions
		layer.stack = parent.stack
		layer.count = parent.count + 1
		layer.size = parent.size
		layer.breaks = make([]*Instruction, 0)
		layer.continues = make([]*Instruction, 0)
	case BLOCK:
		layer.imports = parent.imports
		layer.constants = parent.constants
		layer.instructions = parent.instructions
		layer.functions = parent.functions
		layer.stack = parent.stack
		layer.count = parent.count + 1
		layer.size = parent.size
		layer.breaks = parent.breaks
		layer.continues = parent.continues
	}
	return layer
}

type Translate struct {
	layer *Layer
}

func (translate *Translate) PushInstruction(code int, args []int) *Instruction {
	var instruction = InstructionNew(len(translate.layer.instructions), code, args)
	translate.layer.instructions = append(translate.layer.instructions, instruction)
	return instruction
}

func (translate *Translate) In(description int) {
	translate.layer = LayerNew(translate.layer, description)
}

func (translate *Translate) Out() *Layer {
	var layer = translate.layer
	if translate.layer.parent != nil {
		switch layer.description {
		case FOR:
			translate.layer.parent.imports = layer.imports
			translate.layer.parent.instructions = layer.instructions
			translate.layer.parent.functions = layer.functions
		case BLOCK, FILE:
			translate.layer.parent.imports = layer.imports
			translate.layer.parent.instructions = layer.instructions
			translate.layer.parent.breaks = layer.breaks
			translate.layer.parent.functions = layer.functions
		}
		translate.layer.parent.size = layer.size
		translate.layer = translate.layer.parent
	}
	return layer
}

func (translate *Translate) Parser(s_block *S_Block) *Line {
	translate.In(FILE)
	s_block.Translate(translate)
	translate.Out()
	var constants = make([]interface{}, len(translate.layer.constants))
	for k, v := range translate.layer.constants {
		constants[-1*v-1] = k
	}
	fmt.Println(constants)

	for _, v := range translate.layer.instructions {
		fmt.Println(v)
	}

	for _, v := range translate.layer.functions {
		fmt.Println(v)
	}
	return LineNew(constants, translate.layer.instructions, translate.layer.functions, translate.layer.size)
}

func (translate *Translate) Load(token *Token) int {
	var index, ok = translate.layer.imports[token.value]
	if ok {
		panic(fmt.Sprint(token.value, "已存在"))
	}
	index, ok = translate.layer.variables[token.value]
	if ok {
		return index
	}
	translate.layer.variables[token.value] = translate.layer.count
	translate.layer.count++
	translate.layer.size++
	return translate.layer.count - 1
}

func (translate *Translate) GetConstant(token *Token) int {
	if token.category == TOKEN_ID || token.category == TOKEN_STRING {
		var value, ok = translate.layer.constants[token.value]
		if ok {
			return value
		} else {
			translate.layer.constants[token.value] = -len(translate.layer.constants) - 1
			return translate.layer.constants[token.value]
		}
	} else if token.category == TOKEN_NUMBER {
		var number, err = strconv.ParseFloat(token.value, 64)
		if err != nil {
			panic(fmt.Sprintln(token, "无法转换为数字"))
		}
		var value, ok = translate.layer.constants[number]
		if ok {
			return value
		} else {
			translate.layer.constants[number] = -len(translate.layer.constants) - 1
			return translate.layer.constants[number]
		}
	} else if token.category == TOKEN_NIL {
		var value, ok = translate.layer.constants[nil]
		if ok {
			return value
		} else {
			translate.layer.constants[nil] = -len(translate.layer.constants) - 1
			return translate.layer.constants[nil]
		}
	} else if token.category == TOKEN_FALSE {
		var value, ok = translate.layer.constants[false]
		if ok {
			return value
		} else {
			translate.layer.constants[false] = -len(translate.layer.constants) - 1
			return translate.layer.constants[false]
		}
	} else if token.category == TOKEN_TRUE {
		var value, ok = translate.layer.constants[true]
		if ok {
			return value
		} else {
			translate.layer.constants[true] = -len(translate.layer.constants) - 1
			return translate.layer.constants[true]
		}
	}
	panic("token类型错误")
}

func (translate *Translate) GetAddress(token *Token) int {
	var layer = translate.layer
	var inf = false
	for layer != nil {
		var value, ok = layer.variables[token.value]
		if ok {
			if inf {
				return -1
			}
			return value
		}
		if layer.description == FUNCTION {
			inf = true
		}
		layer = layer.parent
	}
	panic(fmt.Sprint("变量 ", token.value, " 未定义"))
}

func (translate *Translate) PushAddress(address int) {
	if translate.layer.size < translate.layer.count {
		translate.layer.size = translate.layer.count
	}
	translate.layer.stack.Push([]interface{}{address, false})
}

func (translate *Translate) NewAddress() int {
	if translate.layer.size < translate.layer.count+1 {
		translate.layer.size = translate.layer.count + 1
	}
	translate.layer.stack.Push([]interface{}{translate.layer.count, true})
	translate.layer.count++
	return translate.layer.count - 1
}

func (translate *Translate) PeekAddress() int {
	return translate.layer.stack.Back().([]interface{})[0].(int)
}

func (translate *Translate) PopAddress() int {
	var value = translate.layer.stack.Pop().([]interface{})
	if value[1].(bool) {
		translate.layer.count--
	}
	return value[0].(int)
}

type Nonterminal interface {
	Translate(translate *Translate)
}

type S_Block struct {
	statements []interface{}
}

func (s_block *S_Block) Translate(translate *Translate) {
	for i := 0; i < len(s_block.statements); i++ {
		s_block.statements[i].(Nonterminal).Translate(translate)
	}
}

type S_FunctionCall struct {
	s_value *S_Value
}

func (s_tableCall *S_FunctionCall) Translate(translate *Translate) {
	s_tableCall.s_value.Translate(translate)
	translate.PopAddress()
}

type S_For struct {
	s_expression *S_Expression
	token        *Token
	s_value      *S_Value
	s_block      *S_Block
}

func (s_for *S_For) Translate(translate *Translate) {
	var instruction *Instruction
	if s_for.token != nil && s_for.s_value != nil {
		translate.Load(s_for.token)
		s_for.s_value.Translate(translate)
		var iterator = translate.PeekAddress()
		translate.PushInstruction(VM_LOOP, []int{translate.NewAddress(), iterator})
		iterator = translate.PeekAddress()
		var start = len(translate.layer.instructions)
		instruction = translate.PushInstruction(VM_FORIN, []int{translate.NewAddress(), iterator, 0})
		translate.PushInstruction(VM_MOV, []int{translate.GetAddress(s_for.token), translate.PopAddress()})
		translate.In(FOR)
		s_for.s_block.Translate(translate)
		var layer = translate.Out()
		for i := 0; i < len(layer.breaks); i++ {
			layer.breaks[i].args[0] = len(layer.instructions) + 1
		}
		translate.PushInstruction(VM_JUMP, []int{start})
		translate.PopAddress()
		translate.PopAddress()
	} else {
		var start = len(translate.layer.instructions)
		s_for.s_expression.Translate(translate)
		instruction = translate.PushInstruction(VM_JUMP, []int{translate.PopAddress(), 0, 0})
		translate.In(FOR)
		s_for.s_block.Translate(translate)
		var layer = translate.Out()
		for i := 0; i < len(layer.breaks); i++ {
			layer.breaks[i].args[0] = len(layer.instructions) + 1
		}
		translate.PushInstruction(VM_JUMP, []int{start})
	}
	instruction.SetArgs(2, len(translate.layer.instructions))
}

type S_If struct {
	s_expression *S_Expression
	s_block      *S_Block
	s_elseif     []*S_ElseIf
	s_else       *S_Else
}

type S_ElseIf struct {
	s_expression *S_Expression
	s_block      *S_Block
}

type S_Else struct {
	s_block *S_Block
}

func (s_if *S_If) Translate(translate *Translate) {
	var ends = make([]*Instruction, 0)

	s_if.s_expression.Translate(translate)
	var instruction = translate.PushInstruction(VM_JUMP, []int{translate.PopAddress(), 0, 0})
	translate.In(BLOCK)
	s_if.s_block.Translate(translate)
	translate.Out()
	if len(s_if.s_elseif) != 0 || s_if.s_else != nil {
		ends = append(ends, translate.PushInstruction(VM_JUMP, []int{0}))
	}
	instruction.SetArgs(2, len(translate.layer.instructions))
	for i := 0; i < len(s_if.s_elseif); i++ {
		s_if.s_elseif[i].s_expression.Translate(translate)
		var instruction = translate.PushInstruction(VM_JUMP, []int{translate.PopAddress(), 0, 0})
		translate.In(BLOCK)
		s_if.s_elseif[i].s_block.Translate(translate)
		translate.Out()
		if s_if.s_else != nil && i != len(s_if.s_elseif) {
			ends = append(ends, translate.PushInstruction(VM_JUMP, []int{0}))
		}
		instruction.SetArgs(2, len(translate.layer.instructions))
	}
	if s_if.s_else != nil {
		translate.In(BLOCK)
		s_if.s_else.s_block.Translate(translate)
		translate.Out()
	}
	for i := 0; i < len(ends); i++ {
		ends[i].SetArgs(0, len(translate.layer.instructions))
	}
}

type S_Var struct {
	s_value *S_Value
	key     interface{}
}

func (s_var *S_Var) Translate(translate *Translate) {
	if len(s_var.s_value.list) != 0 {
		s_var.s_value.Translate(translate)
	}
}

type S_Assignment struct {
	s_var        *S_Var
	s_expression *S_Expression
}

func (s_assignment *S_Assignment) Translate(translate *Translate) {
	s_assignment.s_var.Translate(translate)
	s_assignment.s_expression.Translate(translate)
	var value = translate.PopAddress()
	if len(s_assignment.s_var.s_value.list) == 0 {
		var token = s_assignment.s_var.key.(*Token)
		var _, ok = translate.layer.imports[token.value]
		if ok {
			panic(fmt.Sprint(token.value, "不可改变"))
		}
		var address = translate.GetAddress(token)
		translate.PushInstruction(VM_MOV, []int{address, value})
	} else {
		switch key := s_assignment.s_var.key.(type) {
		case *S_Get:
			key.s_expression.Translate(translate)
			var address = translate.PopAddress()
			translate.PushInstruction(VM_SETTV, []int{translate.PopAddress(), address, value})
		case *S_Point:
			translate.PushInstruction(VM_SETTV, []int{translate.PopAddress(), translate.GetConstant(key.token), value})
		}
	}
}

type S_DefineAssignment struct {
	token      *Token
	s_nameList *S_NameList
	s_list     *S_List
}

func (s_defineAssignment *S_DefineAssignment) Translate(translate *Translate) {
	var nameList = s_defineAssignment.s_nameList.tokens
	var length = 0
	if s_defineAssignment.s_list != nil {
		s_defineAssignment.s_list.Translate(translate)
		length = s_defineAssignment.s_list.length
	}
	if length == 1 {
		var value = translate.PopAddress()
		for i := 0; i < len(nameList); i++ {
			var name = translate.Load(nameList[i])
			if s_defineAssignment.token.category == TOKEN_CONST {
				translate.layer.imports[nameList[i].value] = value
			}
			translate.PushInstruction(VM_MOV, []int{name, value})
		}
	} else if length > 1 {
		for i := len(nameList) - 1; i >= 0; i-- {
			var value = translate.PopAddress()
			var name = translate.Load(nameList[i])
			if s_defineAssignment.token.category == TOKEN_CONST {
				translate.layer.imports[nameList[i].value] = value
			}
			translate.PushInstruction(VM_MOV, []int{name, value})
		}
	}
}

type S_KeyAssignment struct {
	key   *Token
	value interface{}
}

func (s_keyAssignment *S_KeyAssignment) Translate(translate *Translate) {
	var s_expression = s_keyAssignment.value.(*S_Expression)
	s_expression.Translate(translate)
	var key = translate.GetConstant(s_keyAssignment.key)
	var address = translate.PopAddress()
	translate.PushInstruction(VM_SETTV, []int{translate.PeekAddress(), key, address})
}

type S_NameList struct {
	tokens []*Token
}

type S_List struct {
	s_expressions []*S_Expression
	length        int
}

func (s_list *S_List) Translate(translate *Translate) {
	var start = translate.layer.stack.Len()
	for i := 0; i < len(s_list.s_expressions); i++ {
		s_list.s_expressions[i].Translate(translate)
	}
	s_list.length = translate.layer.stack.Len() - start
}

type S_Expression struct {
	list []interface{}
}

func (s_expression *S_Expression) Translate(translate *Translate) {
	for i := 0; i < len(s_expression.list); i++ {
		var token, ok = s_expression.list[i].(*Token)
		if ok {
			switch token.category {
			case TOKEN_POS:
				var v1 = translate.PopAddress()
				translate.PushInstruction(VM_POS, []int{translate.NewAddress(), v1})
			case TOKEN_NEG:
				var v1 = translate.PopAddress()
				translate.PushInstruction(VM_NEG, []int{translate.NewAddress(), v1})
			case TOKEN_ADD:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_ADD, []int{address, v1, v2})
			case TOKEN_SUB:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_SUB, []int{address, v1, v2})
			case TOKEN_CONCAT:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_CONCAT, []int{address, v1, v2})
			case TOKEN_MUL:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_MUL, []int{address, v1, v2})
			case TOKEN_DIV:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_DIV, []int{address, v1, v2})
			case TOKEN_MOD:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_MOD, []int{address, v1, v2})
			case TOKEN_POWER:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_POWER, []int{address, v1, v2})
			case TOKEN_EQUAL:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_EQ, []int{address, v1, v2})
			case TOKEN_NOTEQUAL:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_NEQ, []int{address, v1, v2})
			case TOKEN_GREATER:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_LT, []int{address, v2, v1})
			case TOKEN_GREATEREQUAL:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_LE, []int{address, v2, v1})
			case TOKEN_LESS:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_LT, []int{address, v1, v2})
			case TOKEN_LESSEQUAL:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_LE, []int{address, v1, v2})
			case TOKEN_AND:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_AND, []int{address, v1, v2})
			case TOKEN_OR:
				var v1 = translate.PopAddress()
				var v2 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_OR, []int{address, v1, v2})
			case TOKEN_NOT:
				var v1 = translate.PopAddress()
				var address = translate.NewAddress()
				translate.PushInstruction(VM_NOT, []int{address, v1})
			case TOKEN_NUMBER, TOKEN_STRING, TOKEN_NIL, TOKEN_FALSE, TOKEN_TRUE:
				var address = translate.GetConstant(token)
				translate.PushAddress(address)
			}
		} else {
			s_expression.list[i].(Nonterminal).Translate(translate)
		}
	}
}

type S_Value struct {
	list []interface{}
}

func (s_value *S_Value) Translate(translate *Translate) {
	var address int
	switch value := s_value.list[0].(type) {
	case *Token:
		address = translate.GetAddress(value)
		if address == -1 {
			translate.PushInstruction(VM_GETUPVAL, []int{translate.NewAddress(), translate.GetConstant(value)})
		} else {
			translate.PushAddress(address)
		}
	case *S_Function:
		value.Translate(translate)
		translate.PushInstruction(VM_CLOSURE, []int{0, 0xffff + len(translate.layer.functions)}).SetArgs(0, translate.NewAddress())
	default:
		value.(Nonterminal).Translate(translate)
		address = translate.PeekAddress()
	}
	if len(s_value.list) > 1 {
		var _, ok = s_value.list[1].(*S_Call)
		if ok {
			translate.PushAddress(address)
		}
	}
	for i := 1; i < len(s_value.list); i++ {
		switch value := s_value.list[i].(type) {
		case *S_Call:
			value.Translate(translate)
			var ok bool
			if i != len(s_value.list)-1 {
				_, ok = s_value.list[i+1].(*S_Call)
				if ok {
					var table = translate.PopAddress()
					translate.NewAddress()
					translate.PushAddress(table)
				}
			}
		default:
			var ok bool
			if i != len(s_value.list)-1 {
				_, ok = s_value.list[i+1].(*S_Call)
				if ok {
					var table = translate.PeekAddress()
					if i == 2 && address == -1 {
						translate.NewAddress()
						address = 0
					}
					translate.PushAddress(table)
				}
			}
			s_value.list[i].(Nonterminal).Translate(translate)
		}
	}
}

type S_Get struct {
	s_expression *S_Expression
}

func (s_get *S_Get) Translate(translate *Translate) {
	s_get.s_expression.Translate(translate)
	var key = translate.PopAddress()
	var table = translate.PopAddress()
	var address = translate.NewAddress()
	translate.PushInstruction(VM_GETTV, []int{address, table, key})
}

type S_Call struct {
	s_valueList *S_ValueList
}

func (s_call *S_Call) Translate(translate *Translate) {
	if len(s_call.s_valueList.s_keyAssignments) != 0 {
		translate.PushInstruction(VM_NEWTABLE, []int{translate.NewAddress(), len(s_call.s_valueList.s_expressions), len(s_call.s_valueList.s_keyAssignments)})
		for i := 0; i < len(s_call.s_valueList.s_expressions); i++ {
			s_call.s_valueList.s_expressions[i].Translate(translate)
			var v = translate.PopAddress()
			var t = translate.PeekAddress()
			translate.PushInstruction(VM_PUSH, []int{t, v})
		}
		for i := 0; i < len(s_call.s_valueList.s_keyAssignments); i++ {
			s_call.s_valueList.s_keyAssignments[i].Translate(translate)
		}
		var args = translate.PopAddress()
		var function = translate.PopAddress()
		var table = translate.PopAddress()
		var address = translate.NewAddress()
		translate.PushInstruction(VM_CALLX, []int{address, table, function, args})
	} else {
		var begin = translate.layer.count
		for i := 0; i < len(s_call.s_valueList.s_expressions); i++ {
			s_call.s_valueList.s_expressions[i].Translate(translate)
			var v = translate.PopAddress()
			translate.PushInstruction(VM_MOV, []int{translate.NewAddress(), v})
		}
		for i := 0; i < len(s_call.s_valueList.s_expressions); i++ {
			translate.PopAddress()
		}
		var function = translate.PopAddress()
		var table = translate.PopAddress()
		translate.PushInstruction(VM_CALL, []int{translate.NewAddress(), table, function, begin, len(s_call.s_valueList.s_expressions)})
	}
}

type S_Point struct {
	token *Token
}

func (s_point *S_Point) Translate(translate *Translate) {
	var table = translate.PopAddress()
	var address = translate.NewAddress()
	translate.PushInstruction(VM_GETTV, []int{address, table, translate.GetConstant(s_point.token)})
}

type S_Function struct {
	s_nameList *S_NameList
	s_block    *S_Block
}

func (s_function *S_Function) Translate(translate *Translate) {
	translate.In(FUNCTION)
	translate.layer.variables["this"] = 0
	translate.layer.count++
	var args []*Token
	if s_function.s_nameList != nil {
		args = s_function.s_nameList.tokens
		for i := 0; i < len(args); i++ {
			translate.layer.variables[args[i].value] = i + 1
		}
		translate.layer.count += len(args)
	}
	translate.layer.size = len(args) + 1
	s_function.s_block.Translate(translate)
	var layer = translate.Out()
	translate.layer.functions = append(translate.layer.functions, FunctionNew(translate.layer, args, layer.instructions, layer.size))
}

type S_ValueList struct {
	s_expressions    []*S_Expression
	s_keyAssignments []*S_KeyAssignment
}

type S_Table struct {
	s_valueList *S_ValueList
}

func (s_table *S_Table) Translate(translate *Translate) {
	var address = translate.NewAddress()
	translate.PushInstruction(VM_NEWTABLE, []int{address, len(s_table.s_valueList.s_expressions), len(s_table.s_valueList.s_keyAssignments)})
	for i := 0; i < len(s_table.s_valueList.s_expressions); i++ {
		s_table.s_valueList.s_expressions[i].Translate(translate)
		var v = translate.PopAddress()
		var t = translate.PeekAddress()
		translate.PushInstruction(VM_PUSH, []int{t, v})
	}
	for i := 0; i < len(s_table.s_valueList.s_keyAssignments); i++ {
		s_table.s_valueList.s_keyAssignments[i].Translate(translate)
	}
}

type S_Return struct {
	s_expression *S_Expression
}

func (s_return *S_Return) Translate(translate *Translate) {
	if s_return.s_expression == nil {
		translate.PushInstruction(VM_RETURN, []int{})
	} else {
		s_return.s_expression.Translate(translate)
		translate.PushInstruction(VM_RETURN, []int{translate.PopAddress()})
	}
}

type S_Import struct {
	name *Token
	from *Token
}

func (s_import *S_Import) Translate(translate *Translate) {
	var name = translate.Load(s_import.name)
	if s_import.from != nil {
		translate.PushInstruction(VM_IMPORT, []int{name, translate.GetConstant(s_import.from)})
	} else {
		translate.PushInstruction(VM_IMPORT, []int{name, translate.GetConstant(s_import.name)})
	}
	translate.layer.imports[s_import.name.value] = name
}
